package com.youxin.chat.pay.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;

import com.youxin.base.BaseResultCode;
import com.youxin.base.TResult;
import com.youxin.common.constant.RedisKey;
import com.youxin.chat.pay.context.CommonOrderContext;
import com.youxin.chat.pay.convert.ChannelPayConvert;
import com.youxin.chat.pay.convert.UserPayConvert;
import com.youxin.chat.pay.entity.dto.CardBindInfo;
import com.youxin.chat.pay.dto.PayRulesDto;
import com.youxin.chat.pay.entity.req.*;
import com.youxin.chat.pay.entity.search.CashSearch;
import com.youxin.chat.pay.entity.search.UserCardSearch;
import com.youxin.chat.pay.entity.vo.ChannelBalanceVo;
import com.youxin.chat.pay.entity.vo.DefrayRateVo;
import com.youxin.chat.pay.enums.CashStatusEnum;
import com.youxin.chat.pay.enums.OrderSourceEnum;
import com.youxin.chat.pay.enums.OrderStatusEnum;
import com.youxin.chat.pay.mapper.*;
import com.youxin.chat.pay.model.*;
import com.youxin.chat.pay.service.PayRulesService;
import com.youxin.chat.pay.service.SequenceService;
import com.youxin.chat.pay.service.TemplateService;
import com.youxin.chat.pay.service.UserPayService;
import com.youxin.chat.pay.service.channel.ChannelRouter;
import com.youxin.chat.pay.service.channel.IPayChannel;
import com.youxin.chat.pay.service.channel.entity.*;
import com.youxin.chat.pay.service.channel.entity.ChannelResponse;
import com.youxin.chat.pay.service.manager.ImManager;
import com.youxin.chat.pay.service.manager.UserCashManager;
import com.youxin.chat.pay.service.manager.UserRechargeManager;
import com.youxin.chat.pay.service.manager.UserWalletManager;
import com.youxin.chat.pay.service.rpc.UserService;
import com.youxin.chat.pay.utils.BankCardUtil;
import com.youxin.chat.user.dto.UserInfoDto;
import com.youxin.dozer.DozerUtils;
import com.youxin.exception.SystemException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.TimeUnit;

@Service
public class UserPayServiceImpl implements UserPayService {

    private static final Logger logger = LoggerFactory.getLogger(UserPayService.class);

    @Resource
    private UserCardMapper userCardMapper;

    @Resource
    private SequenceService sequenceService;

    @Resource
    private UserRechargeMapper userRechargeMapper;

    @Resource
    private BankCardBinMapper bankCardBinMapper;

    @Resource
    private ChannelRouter channelRouter;

    @Resource
    private UserCashMapper userCashMapper;

    @Resource
    private UserWalletMapper userWalletMapper;

    @Resource
    private ChannelOrderMapper channelOrderMapper;

    @Resource
    private ChannelCashMapper channelCashMapper;

    @Resource
    private BankInfoMapper bankInfoMapper;

    @Resource
    private ChannelCardMapper channelCardMapper;

    @Resource
    private CommonOrderMapper commonOrderMapper;

    @Resource
    private ChannelBankMapper channelBankMapper;

    @Resource
    private TransactionDetailMapper transactionDetailMapper;

    @Resource
    private ChannelAccountMapper channelAccountMapper;


    @Resource
    private PayCertificationMapper payCertificationMapper;

    @Resource
    private UserService userService;

    @Resource
    private PayRulesService payRulesService;

    @Resource
    private StringRedisTemplate stringRedisTemplate;


    @Resource
    private ImManager imManager;

    @Resource
    private UserWalletManager userWalletManager;

    @Resource
    private UserRechargeManager userRechargeManager;

    @Resource
    private UserCashManager userCashManager;

    @Resource
    private TemplateService templateService;

    @Resource
    private DozerUtils dozerUtils;

    @Override
    public TResult<String> auditBindCard(UserCardReq userCardDto, String userNo) throws SystemException {
        if (!BankCardUtil.checkBankCard(userCardDto.getCardNo())) {
            logger.error("银行卡号不符合规则，cardNo={}", userCardDto.getCardNo());
            throw new SystemException(BaseResultCode.COMMON_FAIL, "银行卡号不符合规则");
        }
        if (!checkMobile(userCardDto.getCardMobile())) {
            logger.error("手机号不符号规则，mobile={}", userCardDto.getCardMobile());
            throw new SystemException(BaseResultCode.COMMON_FAIL, "手机号码不符号规则");
        }

        int count = payCertificationMapper.selectCount(new LambdaQueryWrapper<PayCertification>().eq(PayCertification::getUserNo, userNo));
        if (count == 1) {
            PayCertification payCertification = payCertificationMapper.selectOne(new LambdaQueryWrapper<PayCertification>().eq(PayCertification::getUserNo, userNo));
            if (!payCertification.getCardId().equalsIgnoreCase(userCardDto.getCardId())) {
                throw new SystemException(BaseResultCode.COMMON_FAIL, "当前证件号与实名认证信息不符");
            }
        }

        ChannelAccount channelAccount = channelRouter.getDefaultChannelAcccount();
        UserCardSearch search = new UserCardSearch();
        search.setChannelAccount(channelAccount.getChannelAccount());
        search.setUserNo(userNo);
        List<CardBindInfo> cardList = userCardMapper.getCardListByParam(search);
        if (!CollectionUtils.isEmpty(cardList)) {
            if (Integer.compare(cardList.size(), 3) > 0) {
                return TResult.build(BaseResultCode.COMMON_FAIL, "用户绑卡卡数不能超过三张");
            }
            for (CardBindInfo item : cardList) {
                if (!item.getCardId().equalsIgnoreCase(userCardDto.getCardId())) {
                    return TResult.build(BaseResultCode.COMMON_FAIL, "同个帐户不能绑定多个身分证");
                }
                if (item.getCardNo().equalsIgnoreCase(userCardDto.getCardNo())) {
                    return TResult.build(BaseResultCode.COMMON_FAIL, "此卡已绑定，无需再绑定");
                }
            }
        }
        if (userCardDto.getCardType() == null) {
            userCardDto.setCardType(0);
        }
        BankCardBin bankCardBin = getCardBinByCardNo(userCardDto.getCardNo());
        if (bankCardBin == null) {
            return TResult.build(BaseResultCode.COMMON_FAIL, "系统不支持该银行卡");
        }
        String bankNo = bankCardBin.getBankCode();
        ChannelBank channelBank = channelBankMapper.selectByChannelCodeAndBankCode(channelAccount.getChannelCode(), bankCardBin.getBankCode(), 0);
        if (channelBank != null) {
            bankNo = channelBank.getChannelBankCode();
        }
        ChannelBindCard channelBindCard = UserPayConvert.buildBindCardInfo(userCardDto, userNo, channelAccount, bankNo);
        IPayChannel payChannel = channelRouter.getPayChannel(channelAccount);
        String orderNo = sequenceService.buildChannelOrderNo();
        ChannelResponse channelResponse = payChannel.bindCard(orderNo, channelBindCard, channelAccount);
        channelBindCard.setExt(channelResponse.getExt());
        if (!BaseResultCode.SUCCESS.equalsIgnoreCase(channelResponse.getCode())) {
            return TResult.build(channelResponse.getCode(), channelResponse.getErrorMsg());
        }
        stringRedisTemplate.opsForValue().set(RedisKey.CHANNEL_ORDER_NO + orderNo, JSONObject.toJSONString(channelBindCard));
        stringRedisTemplate.expire(RedisKey.CHANNEL_ORDER_NO + orderNo, 180, TimeUnit.SECONDS);
        return TResult.success(orderNo);
    }

    @Override
    public TResult<String> validateBindSms(UserCardSmsReq userCardSmsDto, String userNo) throws SystemException {
        ChannelAccount channelAccount = channelRouter.getDefaultChannelAcccount();
        ChannelBindCard channelBindCard = JSONObject.parseObject(stringRedisTemplate.opsForValue().get(RedisKey.CHANNEL_ORDER_NO + userCardSmsDto.getId()), ChannelBindCard.class);
        if (channelBindCard == null) {
            throw new SystemException(BaseResultCode.RECORD_NOT_EXISTS, "认证超时，请重新输入用户信息");
        }

        PayCertification payCertification = payCertificationMapper.selectOne(new LambdaQueryWrapper<PayCertification>().eq(PayCertification::getUserNo, userNo));
        if (payCertification != null && !payCertification.getCardId().equalsIgnoreCase(channelBindCard.getCardId())) {
            throw new SystemException(BaseResultCode.COMMON_FAIL, "当前证件号与实名认证信息不符");
        }

        ChannelBindCardSms channelBindCardSms = UserPayConvert.buildBindCardSms(userCardSmsDto);
        channelBindCardSms.setUserNo(userNo);
        channelBindCardSms.setExt(channelBindCard.getExt());
        IPayChannel payChannel = channelRouter.getPayChannel(channelAccount);

        ChannelResponse channelResponse = payChannel.validateSms(channelBindCardSms, channelAccount);

        if (BaseResultCode.SUCCESS.equalsIgnoreCase(channelResponse.getCode())) {

            BankCardBin bankCardBin = getCardBinByCardNo(channelBindCard.getCardNo());

            UserCard userCard = UserPayConvert.buildUserCard(channelBindCard, userNo, bankCardBin.getBankCode(), userCardSmsDto.getUserIp());
            UserCard info = userCardMapper.selectByCardNoAndUserNo(userNo, userCard.getCardNo());
            String id;
            if (!Optional.ofNullable(info).isPresent()) {
                userCardMapper.insert(userCard);
                id = userCard.getId();
            } else {
                id = info.getId();
            }
            ChannelCard channelCard = UserPayConvert.buildChannelCard(id, channelResponse.getAgreementId(), channelBindCard.getBankNo(), channelAccount, channelResponse.getExt());

            channelCardMapper.insert(channelCard);

            if (payCertification == null) {
                payCertification = UserPayConvert.buildPayCertification(channelBindCard);
                payCertificationMapper.insert(payCertification);
            }
            stringRedisTemplate.delete(RedisKey.CHANNEL_ORDER_NO + userCardSmsDto.getId());
        } else {
            logger.error("用户绑卡认证失败,error={}", channelResponse.getResult());
            return TResult.build(channelResponse.getCode(), channelResponse.getErrorMsg());

        }
        return TResult.success();
    }

    @Override
    public TResult<String> doCharge(UserChargeReq userChargeDto, String userNo, String userIp) throws SystemException {
        String pass = userWalletManager.getPayPwd(userNo);
        boolean b = userWalletManager.validatePass(userNo, userChargeDto.getPayPwd(), pass);
        if (!b) {
            return TResult.build(BaseResultCode.COMMON_FAIL, "支付密码不正确");
        }
        if (userChargeDto.getAmount().compareTo(new BigDecimal(0)) < 0) {
            return TResult.build(BaseResultCode.COMMON_FAIL, "金额不能为负数");
        }
        PayRulesDto payRulesDto = payRulesService.info();
        if (payRulesDto.getRechargeOnceMin().compareTo(userChargeDto.getAmount()) > 0) {
            logger.error("用户单笔充值金额不能小于{},实现充值金额={},userNo={}", payRulesDto.getRechargeOnceMin().divide(new BigDecimal(100)), userChargeDto.getAmount().divide(new BigDecimal(100)), userNo);
            throw new SystemException(BaseResultCode.EXCEED_SINGLE_MIN, "充值金额小于单笔最小金额");
        }
        ChannelAccount channelAccount = channelRouter.getDefaultChannelAcccount();
        IPayChannel payChannel = channelRouter.getPayChannel(channelAccount);

        UserCard userCard = userCardMapper.selectById(userChargeDto.getCardId());
        UserCardDto userCardDto = dozerUtils.map(userCard, UserCardDto.class);
        ChannelBank channelBank = channelBankMapper.selectByChannelCodeAndBankCode(channelAccount.getChannelCode(), userCardDto.getBankCode(), 0);
        if (channelBank != null) {
            userCardDto.setBankCode(channelBank.getChannelBankCode());
        }
        if (userCard.getIsDeleted() != 0) {
            throw new SystemException(BaseResultCode.RECORD_NOT_EXISTS, "卡号不存在");
        }
        if (!userNo.equalsIgnoreCase(userCard.getUserNo())) {
            throw new SystemException(BaseResultCode.HAS_NO_PERMISSION, "用户与卡号不一致");
        }
        checkRechargeRisk(userNo, userChargeDto.getAmount());
        UserInfoDto userInfoDto = userService.getByUserNo(userNo);
        String orderNo = sequenceService.buildUserOrderNo();
        UserRecharge userRecharge = UserPayConvert.buildRecharge(userChargeDto, userCard, orderNo, userNo, userIp);
        String channelOrderNo = sequenceService.buildChannelOrderNo();

        ChannelOrder channelOrder = UserPayConvert.buildChannelOrder(userRecharge, channelAccount, channelOrderNo);
        CommonOrder commonOrder = CommonOrderContext.buildPayCommonOrder(userRecharge, userInfoDto.getAgentNo());
        userRechargeManager.saveRechargeInfo(userRecharge, channelOrder, commonOrder);
        ChannelCard channelCard = channelCardMapper.selectByCardIdAndChannelAccount(userCard.getId(), channelAccount.getChannelAccount());
        String contractId = null;
        if (channelCard != null) {
            contractId = channelCard.getContractId();

        }

        ChannelPay channelPay = ChannelPayConvert.transOrderToChannelPay(channelOrder, contractId, userCardDto, channelCard.getExpand());
        ChannelResponse channelResponse = payChannel.pay(channelPay, channelAccount);
        //  channelPay.setExt(channelResponse.getExt());
        if (!BaseResultCode.SUCCESS.equalsIgnoreCase(channelResponse.getCode())) {
            return TResult.build(channelResponse.getCode(), channelResponse.getErrorMsg());
        }
        if (!StringUtils.isEmpty(channelResponse.getExt())) {
            stringRedisTemplate.opsForValue().set(RedisKey.CHANNEL_CHARGE_SMS + orderNo, channelResponse.getExt(), 10, TimeUnit.MINUTES);
        }
        int validateSms = 0;
        if (!StringUtils.isEmpty(channelAccount.getExtInfo())) {
            JSONObject extObject = JSONObject.parseObject(channelAccount.getExtInfo());
            validateSms = Optional.ofNullable(extObject.getInteger("validateSms")).orElse(0);
        }
        //  RechargeResponse rechargeResponse = new RechargeResponse(orderNo, validateSms);
        return TResult.success(orderNo);
    }

    @Override
    public TResult<String> terminationCard(TerminationCardReq terminationCardDto, String userNo) throws SystemException {
        String pass = userWalletManager.getPayPwd(userNo);
        boolean b = userWalletManager.validatePass(userNo, terminationCardDto.getPayPass(), pass);
        if (!b) {
            return TResult.build(BaseResultCode.COMMON_FAIL, "支付密码不正确");
        }

        UserCard userCard = userCardMapper.selectById(terminationCardDto.getId());
        userCardMapper.deleteById(userCard.getId());
        List<ChannelCard> channelCardList = channelCardMapper.selectByCardId(terminationCardDto.getId());
        for (ChannelCard channelCard : channelCardList) {
            ChannelAccount channelAccount = channelRouter.getChannelAcccountByAccountNo(channelCard.getChannelAccount());
            ChannelTerminationCard channelTerminationCard = UserPayConvert.buildTerminationInfo(userCard, channelCard.getContractId(), channelAccount);
            IPayChannel payChannel = channelRouter.getPayChannel(channelAccount);
            ChannelResponse channelResponse = payChannel.termination(channelTerminationCard, channelAccount);
            if (!BaseResultCode.SUCCESS.equalsIgnoreCase(channelResponse.getCode())) {
                continue;
            }
            channelCardMapper.deleteById(channelCard.getId());
        }
        return TResult.success();
    }

    @Override
    public TResult<String> payQuery(String rechargeNo) throws SystemException {
        ChannelAccount channelAccount = channelRouter.getDefaultChannelAcccount();
        IPayChannel payChannel = channelRouter.getPayChannel(channelAccount);
        UserRecharge userRecharge = userRechargeMapper.selectOne(new LambdaQueryWrapper<UserRecharge>().eq(UserRecharge::getRechargeNo, rechargeNo));
        if (userRecharge.getRechargeStatus().equals(OrderStatusEnum.PAY_SUCCESS.getStatus())) {
            return TResult.success();
        }
        ChannelOrder channelOrder = channelOrderMapper.selectOne(new LambdaQueryWrapper<ChannelOrder>().eq(ChannelOrder::getRechrageNo, rechargeNo));
        if (channelOrder == null) {
            return TResult.build(BaseResultCode.COMMON_FAIL, "查询请求异常");

        }
        ChannelResponse channelResponse = payChannel.payQuery(channelOrder, channelAccount);
        if (channelResponse.getCode().equalsIgnoreCase(BaseResultCode.COMMON_FAIL)) {
            return TResult.build(BaseResultCode.COMMON_FAIL, "查询请求异常");
        }
        Integer status = channelResponse.getStatus();
        if (OrderStatusEnum.PAY_SUCCESS.getStatus().equals(status)) {
            userRechargeManager.doPaySuccess(channelOrder);
            templateService.sendChargeSuccMessage(channelOrder.getChannelOrderNo());

            return TResult.success();
        } else if (OrderStatusEnum.PAY_FAIL.getStatus().equals(status)) {
            userRechargeManager.doPayFail(channelOrder);
            return TResult.build(channelResponse.getCode(), channelResponse.getErrorMsg());

        }

        return TResult.success();
    }

    @Override
    public TResult<String> defray(UserCashReq userCashReq, String userNo) throws SystemException {

        userCashManager.checkUserRate(userNo);
        try {
            String pass = userWalletManager.getPayPwd(userNo);
            boolean b = userWalletManager.validatePass(userNo, userCashReq.getPayPwd(), pass);
            if (!b) {
                return TResult.build(BaseResultCode.COMMON_FAIL, "支付密码不正确");
            }
            checkDefraykRisk(userNo, userCashReq.getAmount());
            ChannelAccount channelAccount = channelRouter.getDefaultChannelAcccount();
            IPayChannel payChannel = channelRouter.getPayChannel(channelAccount);
            UserCard userCard = userCardMapper.selectById(userCashReq.getCardId());
            if (userCard == null) {
                return TResult.build(BaseResultCode.COMMON_FAIL, "用户绑卡信息有误，请联系技术人员");
            }
            if (!userNo.equalsIgnoreCase(userCard.getUserNo())) {
                return TResult.build(BaseResultCode.COMMON_FAIL, "该用户无权提到该银行卡");

            }
            String orderNo = sequenceService.buildUserCashNo();
            PayRulesDto payRules = payRulesService.info();
            if (userCashReq.getAmount().compareTo(payRules.getPerMinCashAmount()) == -1) {
                return TResult.build(BaseResultCode.COMMON_FAIL, "不能低于最小提现金额");
            }

            UserCash userCash = UserPayConvert.builddefrayUserCash(userCashReq, channelAccount, userCard, userNo, orderNo, payRules);
            UserInfoDto userInfoDto = userService.getByUserNo(userNo);

            CommonOrder commonOrder = CommonOrderContext.buildDefrayCommonOrder(userCashReq, userNo, orderNo, userInfoDto.getAgentNo());
            ChannelCash channelCash = UserPayConvert.buildDefrayChannelCash(userCard, userCash, userNo, orderNo, channelAccount);
            //修改数据库表
            userCashManager.doCashProcessing(userCash, commonOrder, channelCash);

            ChannelBank channelBank = channelBankMapper.selectByChannelCodeAndBankCode(channelAccount.getChannelCode(), userCard.getBankCode(), 1);

            String bankCode = userCard.getBankCode();
            if (channelBank != null) {
                bankCode = channelBank.getChannelBankCode();
            }
            String bankName = "";
            BankInfo bankInfo = bankInfoMapper.selectOne(new LambdaQueryWrapper<BankInfo>().eq(BankInfo::getBankCode, userCard.getBankCode()));
            if (bankInfo != null) {
                bankName = bankInfo.getBankName();
            } else {
                return TResult.build(BaseResultCode.COMMON_FAIL, "没有改银行信息");
            }
            String bankLinked = "";
            BankCardBin bankCardBin = getCardBinByCardNo(userCard.getCardNo());
            if (bankCardBin != null) {
                bankLinked = bankCardBin.getCardBinNo();
            } else {
                return TResult.build(BaseResultCode.COMMON_FAIL, "没有改银行信息");
            }

            ChannelDefray channelDefray = UserPayConvert.buildDefrayInfo(channelCash, userCard, bankCode, bankLinked, bankName);

            ChannelResponse channelResponse = payChannel.defray(channelDefray, channelAccount);
            if (OrderStatusEnum.PAY_FAIL.getStatus().equals(channelResponse.getStatus())) {
                userCashManager.doCashFail(userCash);
                return TResult.build(channelResponse.getCode(), channelResponse.getErrorMsg());

            }

            if (!channelResponse.getCode().equalsIgnoreCase(BaseResultCode.SUCCESS)) {
                return TResult.build(channelResponse.getCode(), channelResponse.getErrorMsg());
            }
        } catch (SystemException e) {
            userCashManager.removeUserRate(userNo);
            throw e;
        }
        return TResult.success();
    }

    @Override
    public TResult<String> defrayQuery(String cashNo) throws SystemException {
        ChannelAccount channelAccount = channelRouter.getDefaultChannelAcccount();
        IPayChannel payChannel = channelRouter.getPayChannel(channelAccount);
        UserCash userCash = userCashMapper.selectOne(new LambdaQueryWrapper<UserCash>().eq(UserCash::getCashNo, cashNo));
        ChannelResponse channelResponse = payChannel.defrayQuery(userCash, channelAccount);
        if (BaseResultCode.COMMON_FAIL.equalsIgnoreCase(channelResponse.getCode())) {
            return TResult.build(BaseResultCode.COMMON_FAIL, "查询请求异常");
        }
        Integer status = channelResponse.getStatus();
        if (CashStatusEnum.SUCCESS.getStatus().equals(status)) {
            //TODO 更改订单状态
            userCash.setCashStatus(Integer.valueOf(status));
            userCashManager.doCashSuccess(userCash);
            sendOrderNotify(userCash.getCashNo());
            TResult result = TResult.success();
            result.setData(channelResponse.getResult());
            return result;
        } else if (CashStatusEnum.FAIL.getStatus().equals(status)) {
            userCash.setCashStatus(Integer.valueOf(status));
            userCashManager.doCashFail(userCash);
            //sendOrderNotify(userCash.getCashNo());
            TResult result = TResult.build(channelResponse.getCode(), channelResponse.getErrorMsg());
            result.setData(channelResponse.getResult());
            return result;
        }

        return TResult.success();
    }

    @Override
    public TResult<String> rechargeNotify(ChannelNotifyRequest notifyRequest) throws SystemException {
        logger.info("notifyRequest={}", notifyRequest.getData());
        String orderNo = notifyRequest.getOrderNo();
        ChannelOrder channelOrder = channelOrderMapper.selectByChannelOrderNo(notifyRequest.getOrderNo());
        if (channelOrder == null) {
            logger.error("订单不存在，orderNo={}", notifyRequest.getOrderNo());
            throw new SystemException(BaseResultCode.COMMON_FAIL, "订单不存在");
        }
        ChannelAccount channelAccount = channelRouter.getChannelAcccountByAccountNo(channelOrder.getChannelAccount());
        IPayChannel payChannel = channelRouter.getPayChannel(channelAccount);
        ChannelNotifyResponse response = payChannel.orderNotify(notifyRequest, channelAccount);
        if (OrderStatusEnum.PAY_SUCCESS.getStatus().equals(response.getStatus())) {
            userRechargeManager.doPaySuccess(channelOrder);
            templateService.sendChargeSuccMessage(orderNo);
        } else if (OrderStatusEnum.PAY_FAIL.getStatus().equals(response.getStatus())) {
            userRechargeManager.doPayFail(channelOrder);
        }
        return TResult.success(response.getResponseText());
    }

    @Override
    public TResult<String> defrayNotify(ChannelNotifyRequest notifyRequest) throws SystemException {
        ChannelCash channelCash = channelCashMapper.selectOne(new LambdaQueryWrapper<ChannelCash>().eq(ChannelCash::getCashNo, notifyRequest.getOrderNo()));
        if (channelCash == null) {
            logger.error("订单不存在，orderNo={}", notifyRequest.getOrderNo());
            throw new SystemException(BaseResultCode.COMMON_FAIL, "订单不存在");
        }
        ChannelAccount channelAccount = channelRouter.getChannelAcccountByAccountNo(channelCash.getChannelAccount());
        IPayChannel payChannel = channelRouter.getPayChannel(channelAccount);
        ChannelNotifyResponse channelNotifyResponse = payChannel.defrayNotify(notifyRequest, channelAccount);
        UserCash userCash = userCashMapper.selectByCashNo(channelCash.getCashNo());
        if (OrderStatusEnum.PAY_SUCCESS.getStatus().equals(channelNotifyResponse.getStatus())) {
            userCashManager.doCashSuccess(userCash);
            sendOrderNotify(userCash.getCashNo());

        } else if (OrderStatusEnum.PAY_FAIL.getStatus().equals(channelNotifyResponse.getStatus())) {
            userCashManager.doCashFail(userCash);
        }
        return TResult.success(channelNotifyResponse.getResponseText());
    }

    @Override
    public TResult<String> doChargeSms(UserChargeSmsReq userChargeSmsDto, String userNo) throws SystemException {
        ChannelAccount channelAccount = channelRouter.getDefaultChannelAcccount();
        String extStr = stringRedisTemplate.opsForValue().get(RedisKey.CHANNEL_CHARGE_SMS + userChargeSmsDto.getId());
        JSONObject ext = Optional.ofNullable(extStr).map(JSONObject::parseObject).orElse(new JSONObject());
        logger.info("读取短信保存数据，data={}", ext.toJSONString());
        ChannelOrder channelOrder = channelOrderMapper.selectOne(new LambdaQueryWrapper<ChannelOrder>().eq(ChannelOrder::getRechrageNo, userChargeSmsDto.getId()).eq(ChannelOrder::getUserNo, userNo));

        IPayChannel payChannel = channelRouter.getPayChannel(channelAccount);

        ChannelChargeSms channelChargeSms = UserPayConvert.buildChannelChargeSms(userChargeSmsDto, userNo, channelOrder.getChannelOrderNo(), ext);
        ChannelResponse channelResponse = payChannel.doChargeSms(channelChargeSms, channelAccount);

        if (!BaseResultCode.SUCCESS.equalsIgnoreCase(channelResponse.getCode())) {
            logger.error("用户确认支付失败,error={}", channelResponse.getResult());
            return TResult.build(channelResponse.getCode(), channelResponse.getErrorMsg());
        }
        return TResult.success();
    }

    @Override
    public DefrayRateVo defrayRate(String amount, String userNo) throws SystemException {
        DefrayRateVo defrayRateDto = new DefrayRateVo();
        PayRulesDto payRulesDto = payRulesService.info();
        String rate = String.valueOf(payRulesDto.getCashRate().divide(new BigDecimal(100)));
        BigDecimal cashAmount = new BigDecimal(amount).subtract(new BigDecimal(amount).multiply(new BigDecimal(rate))).subtract(payRulesDto.getMinDefrayFee());
        defrayRateDto.setRate(rate);
        defrayRateDto.setCashAmount(cashAmount);
        defrayRateDto.setMaxCashAmount(payRulesDto.getMaxDayCashAmount());
        defrayRateDto.setServiceFee(payRulesDto.getMinDefrayFee());
        defrayRateDto.setMinCashAmount(payRulesDto.getPerMinCashAmount());
        return defrayRateDto;
    }

    @Override
    public TResult<ChannelBalanceVo> balanceQuery(String merchantId) throws SystemException {
        ChannelBalanceVo balanceDto = new ChannelBalanceVo();
        ChannelAccount channelAccount = channelRouter.getChannelAcccountByAccountNo(merchantId);
        IPayChannel payChannel = channelRouter.getPayChannel(channelAccount);
        ChannelResponse channelResponse = payChannel.balanceQuery(channelAccount);
        if (!BaseResultCode.SUCCESS.equalsIgnoreCase(channelResponse.getCode())) {
            return TResult.build(channelResponse.getCode(), channelResponse.getErrorMsg());
        } else {
            JSONObject jsonObject = JSONObject.parseObject(channelResponse.getExt());
            balanceDto.setAvailableBalance(String.valueOf(new BigDecimal(jsonObject.getString("availableBalance")).divide(new BigDecimal(100))));
            balanceDto.setBalance(String.valueOf(new BigDecimal(jsonObject.getString("balance")).divide(new BigDecimal(100))));
            balanceDto.setLockAmount(String.valueOf(new BigDecimal(jsonObject.getString("lockAmount")).divide(new BigDecimal(100))));
        }
        return TResult.success(balanceDto);
    }

    @Override
    public PayRulesDto getPayRules() throws SystemException {
        return payRulesService.info();
    }


    private void sendOrderNotify(String orderNo) {
        templateService.sendDefraySuccMessage(orderNo);
    }


    private boolean checkMobile(String mobile) {
        if (StringUtils.isEmpty(mobile)) {
            return false;
        }
        if (!mobile.startsWith("1") || mobile.length() != 11) {
            return false;
        }
        return true;
    }

    private BankCardBin getCardBinByCardNo(String cardNo) {
        List<BankCardBin> list = bankCardBinMapper.selectByCardBinNo(cardNo.substring(0, 6));
        if (CollectionUtils.isEmpty(list)) {
            list = bankCardBinMapper.selectByCardBinNo(cardNo.substring(0, 5));

            if (CollectionUtils.isEmpty(list)) {
                list = bankCardBinMapper.selectByCardBinNo(cardNo.substring(0, 7));
            }
        }
        if (CollectionUtils.isEmpty(list)) {
            BankCardBin bankCardBin = new BankCardBin();
            bankCardBin.setBankCode("0");
            bankCardBin.setBankShortName("unknow");
            return bankCardBin;
        }
        return list.get(0);
    }

    /**
     * 充值风控判断
     *
     * @param userNo
     * @param amount
     */
    private void checkRechargeRisk(String userNo, BigDecimal amount) {

        PayRulesDto payRulesDto = payRulesService.info();
        BigDecimal rechargeOnceLimit = payRulesDto.getRechargeOnceLimit();
        if (rechargeOnceLimit.compareTo(amount) < 0) {
            logger.error("用户单笔充值金额不能大于{},实现充值金额={},userNo={}", rechargeOnceLimit.divide(new BigDecimal(100)), amount.divide(new BigDecimal(100)), userNo);
            throw new SystemException(BaseResultCode.EXCEED_SINGLE_MAX, "充值金额超出单笔最大金额");
        }
        BigDecimal maxDayAmount = payRulesDto.getRechargeDayLimit();
        LocalDateTime startTime = LocalDate.now().atStartOfDay();
        LocalDateTime endTime = LocalDateTime.now();
        BigDecimal actualDayAmount = userRechargeMapper.selectDayRechargeAmount(userNo, startTime, endTime);
        actualDayAmount = actualDayAmount == null ? new BigDecimal(0) : actualDayAmount;
        actualDayAmount = actualDayAmount.add(amount);
        if (maxDayAmount.compareTo(actualDayAmount) < 0) {
            logger.error("用户当日充值不能大于{},实现充值金额={}，userNo={}", actualDayAmount.divide(new BigDecimal(100)), amount.divide(new BigDecimal(100)), userNo);
            throw new SystemException(BaseResultCode.EXCEED_DAY_MAX, "充值金额超出当日最大金额");
        }
    }

    /**
     * 提现风控判断
     *
     * @param userNo
     * @param amount
     */
    private void checkDefraykRisk(String userNo, BigDecimal amount) {
        if (amount.compareTo(new BigDecimal(0)) < 0) {
            throw new SystemException(BaseResultCode.COMMON_FAIL, "金额不能为负数");
        }
        int count = transactionDetailMapper.selectCount(new LambdaQueryWrapper<TransactionDetail>()
                .eq(TransactionDetail::getOpType, OrderSourceEnum.PAY)
                .eq(TransactionDetail::getUserNo, userNo));
        if (count < 1) {
            logger.error("，无法提现，userNo={}", userNo);
            throw new SystemException(BaseResultCode.EXCEED_LIMIT, "未充值不许提现");
        }
        PayRulesDto payRulesDto = payRulesService.info();
        BigDecimal perCashAmount = payRulesDto.getPerCashAmount();
        if (perCashAmount.compareTo(amount) < 0) {
            logger.error("用户单笔提现金额不能大于{},实现提现金额={},userNo={}", perCashAmount.divide(new BigDecimal(100)), amount.divide(new BigDecimal(100)), userNo);
            throw new SystemException(BaseResultCode.EXCEED_SINGLE_MAX, "提现金额超出单笔最大金额");
        }
        if (payRulesDto.getPerMinCashAmount().compareTo(amount) > 0) {
            logger.error("用户单笔提现金额不能小于{},实现提现金额={},userNo={}", payRulesDto.getPerMinCashAmount().divide(new BigDecimal(100)), amount.divide(new BigDecimal(100)), userNo);
            throw new SystemException(BaseResultCode.EXCEED_SINGLE_MIN, "提现金额不能小于单笔最小金额" + payRulesDto.getPerMinCashAmount().divide(new BigDecimal(100)) + "元");

        }
        BigDecimal maxDayAmount = payRulesDto.getMaxDayCashAmount();
        CashSearch search = new CashSearch();
        search.setUserNo(userNo);
        search.setStatuses(Arrays.asList(CashStatusEnum.SUCCESS.getStatus()));
        search.setStartTime(LocalDate.now().atStartOfDay());
        search.setEndTime(LocalDateTime.now());
        BigDecimal actualDayAmount = userCashMapper.selectMaxAmountByDay(search);
        actualDayAmount = actualDayAmount == null ? new BigDecimal(0) : actualDayAmount;
        actualDayAmount = actualDayAmount.add(amount);
        if (maxDayAmount.compareTo(actualDayAmount) < 0) {
            logger.error("用户当日提现金额不能大于{},实现提现金额={}，userNo={}", perCashAmount.divide(new BigDecimal(100)), amount.divide(new BigDecimal(100)), userNo);
            throw new SystemException(BaseResultCode.EXCEED_DAY_MAX, "提现金额超出当日最大金额");
        }
        int maxCount = payRulesDto.getMaxDayCount();
        int acutalDayCount = userCashMapper.selectCountByDay(search);
        if (maxCount <= acutalDayCount) {
            logger.error("用户已超出当天最大次数,userNo={}", userNo);
            throw new SystemException(BaseResultCode.EXCEED_DAY_MAX, "提现次数超出当日最大次数" +
                    "");
        }
        UserWallet userWallet = userWalletMapper.selectUserWalleByUserNo(userNo);
        logger.info("用户提现前当前余额为,userNo={},amount={}", userNo, userWallet.getUserBalance());

        if (userWallet.getWalletStatus() != 0) {
            throw new SystemException(BaseResultCode.BALANCE_NOT_ENOUGH, "帐号被封");
        }
        if (userWallet.getUserBalance().compareTo(amount) < 0) {
            logger.error("用户可提现余额不足,balance={},amount={},userNo={}", userWallet.getUserBalance(), amount, userNo);
            throw new SystemException(BaseResultCode.BALANCE_NOT_ENOUGH, "用户余额不足");
        }
    }

}
